#include <map>
#include <list>
#include <set>
#include <sstream>

#include <string.h>
#include <stdint.h>

#include <pthread.h>

#include <openssl/ssl.h>

typedef struct sockaddr sockaddr_t;
typedef struct sockaddr_in sockaddr_in_t;

// --------------------------------------------------------------------------------

class Client;

class BinStream {
public:
  class Plain {
  public:
    Plain(const char* s) {
      _data = s;
    }

    const char* data() const {
      return _data;
    }

  private:
    const char* _data;    
  };

  BinStream& operator<<(uint32_t v) {
    add((char*)&v, sizeof(uint32_t));
    return *this;
  }

  BinStream& operator<<(const char* s) {
    uint32_t len = strlen(s);

    add((char*)&len, sizeof(uint32_t));
    add(s, len);

    return *this;
  }

  BinStream& operator<<(const Plain& plain) {
    add(plain.data(), strlen(plain.data()));
    return *this;
  }

  uint32_t size() const {
    return _stream.str().size();
  }

  const char* data() const {
    return _stream.str().c_str();
  }

private:
  void add(const char* s, size_t len) {
    for (size_t i = 0; i < len; ++i) {
      _stream << s[i];
    }
  }

  std::stringstream _stream;
};

// --------------------------------------------------------------------------------

class Server {
  typedef std::list<Client*> Clients;
  typedef std::list<std::string> Messages;

public:
  Server();
  ~Server();
  
  bool isOk() const;

  int mainLoop();

  void handle(Client* client, const char* msg);

  void remove(Client*);

private:
  void handleBroadcast(const char* msg, const sockaddr_t* addr);

  void replyBroadcast(const char* msg, size_t len, const sockaddr_t* addr);

  void addClient(Client* client);

  void history(uint32_t id, BinStream& s);

  void lockMsg();
  void unlockMsg();

private:
  int svr_sock, bcast_sock;
  bool _initialized;

  Clients _clients;
  Messages _messages;

  pthread_spinlock_t _lock, _msgLock;

  std::set<std::string> _clientNames;

  SSL_CTX* _sslCtx;
};

// --------------------------------------------------------------------------------

class Client {
public:
  Client(int sock, SSL_CTX* sslCtx);
  ~Client();

  void sendPlain(const void* data, uint32_t len);
  void send(const void* data, uint32_t len);
  void send(const BinStream& s);

  void setName(const std::string& name) {
    _name = name;
  }

  const std::string& name() const {
    return _name;
  }

  void authenticate() {
    _auth = true;
  }

  bool authenticated() const {
    return _auth;
  }

  void deauthenticate() {
    _deauth = true;
  }

  bool isOk() const {
    return _ready;
  }

private:
  static void* worker(void* arg);

  int _sock;
  pthread_t _worker;
  std::string _name;

  bool _auth;
  bool _deauth;
  bool _ready;

  SSL* _sslConn;
};

// --------------------------------------------------------------------------------

